#-*- coding: utf-8  -*-
#!/usr/local/bin/python

__author__ = 'jian.yao'

import  math, random
from django.db.models import Q
from django.db.models.query import QuerySet

class BaseManager(object):
    '''
    1、DB操作(backends)的基类，涉及到DB字段的参数，一般采用下划线“_”作为前缀加字段名的方式
    2、继承类需要自行扩展__save__方法
    3、添加了多DB的支持（多DB部署暂时用不到）
    '''
    def __init__(self, _model, _order_type = None):
        self.__model__  = _model
        self.__order_type__ = _order_type

    @property
    def model_class(self):
        return self.__model__

    @property
    def order_type(self):
        return self.__order_type__

    def build_object(self, **kwargs):
        _object = self.model_class()
        return self.__save__(_object, _commit = False, **kwargs)

    def build_id(self, **kwargs):
        return None

    def add(self, _object = None, **kwargs):
        '''
        新增数据项
        @param _object:要添加的数据项，如果不传入_object, 则创建之，如果是自定义ID字段，需要kargs中传入 _id
        '''
        if not _object:
            _object = self.model_class()
        return self.__save__(_object, **kwargs)

    def update(self, _object = None, **kwargs):
        '''
        更新数据项
        @param _object:要更新的数据项，如果不传入_object, 则根据kargs中的 _id 参数查找
        '''
        if not _object:
            _id = kwargs.pop("_id", None)
            _object = self.get(_id = _id)

        if not _object:
            return None
        return self.__save__(_object, **kwargs)

    def add_or_update(self, _object = None, **kwargs):
        '''
        添加或者更新数据项
        @param _object: 要操作的对象
        '''
        _object = self.update(_object, **kwargs)

        if not _object:
            _object = self.add(_object, **kwargs)

        return _object

    def __save__(self, _object = None, _commit = True, **kwargs):
        '''
        save DB操作，各manager自行扩展
        @param _object: 要保存的对象实例
        '''

        raise Exception("Not Implemented.")

    def get(self, _id):
        '''
        根据ID查询对象
        @param _id: ID
        '''
        _object = None
        try:
            _object = self.model_class.objects.get(pk = _id)
        except self.model_class.DoesNotExist:
            _object = None
        return _object

    def query(self, _query_set = None, **kwargs):
        '''
        根据指定参数查询DB
        ××此处仅作初步处理（根据 _id/_pk过滤），继承后进行扩展或者重写
        @param _query_set:之前的查询结果集
        '''
        _objects = None

        if isinstance(_query_set, QuerySet):
            _objects = _query_set
        else:
            _objects = self.model_class.objects.all()

        _order_by = kwargs.pop("_order_by", None)
        _query = Q()
        if '_pk' in kwargs:
            _query = _query & Q(pk = kwargs['_pk'])
        if '_id' in kwargs:
            _query = _query & Q(id = kwargs['_id'])

        _objects = _objects.filter(_query)

        if _order_by and _objects is not None and _order_by.count():
            _objects = self.ordered(_query_set = _objects, _order_by = _order_by)

        return _objects

    def query_raw(self, _raw_query, _params = None, *args, **kargs):
        '''
        Sql查询方式
        @param _raw_query: 查询Sql字符串，参数值用%s等传入
        @param _params: 格式Sql字符串的参数列表
        '''
        if not _raw_query:
            return None
        else:
            return self.model_class.objects.raw(raw_query=_raw_query, params=_params, *args, **kargs)

    def query_ids(self, _ids, _query_set = None):
        '''
        根据ID列表查询内容
        @param _ids: ID List
        @param _query_set:之前的查询结果集
        '''
        if not isinstance(_ids, list):
            return None

        _objects = None

        if isinstance(_query_set, QuerySet):
            _objects = _query_set
        else:
            _objects = self.model_class.objects.all()

        _objects = _objects.filter(id__id = _ids)

        return _objects

    def query_keywords(self, _query_set = None, _keywords = None, **kargs):
        '''
        关键字查询，此处未做任何过滤，请继承后自行处理
        @param _query_set:之前的查询结果集
        @param _keywords:查询关键字
        '''
        if not _keywords:
            return _query_set

        _objects = self.query(_query_set = _query_set, **kargs)
        __keywords = []
        if isinstance(_keywords, basestring):
            __keywords = _keywords.strip().split(' ')
        elif isinstance(_keywords, list):
            _keywords = _keywords

        _query = Q()
        if _query:
            _objects = _objects.filter(_query)

        return _objects

    def query_around(self, _query_set = None, _lat = None, _lon = None, _range = None, _keywords = None, _longitude_field = 'longitude', _latitude_field = 'latitude', **kargs):
        '''
        周边查询
        @param _query_set:之前的查询结果集
        @param _lat: 查询点维度
        @param _lon: 查询点经度
        @param _range: 查询范围
        @param _longitude_field: DB经度字段名
        @param _latitude_field: DB维度字段名
        '''
        if not _lat or not _lon or _range < 0:
            return None

        lon = float(_lon)
        lat = float(_lat)
        offset = math.fabs(float(_range))

        _objects = None
        if isinstance(_query_set, QuerySet):
            _objects = _query_set
        else:
            _objects = self.model_class.objects.all()
        if  _objects is None:
            return None

        _order_by = kargs.pop("_order_by", None)

        if _objects is not None and _objects.count() and kargs:
            _objects = self.query(_query_set=_objects, **kargs)
            if _objects is None:
                return None

        _off = offset/100000.0
        _rect_lon = "%(longitude_field)s > %(longitude_min)s \
                        and %(longitude_field)s < %(longitude_max)s" \
                    % {"longitude_field":_longitude_field, "longitude_min":lon-_off, \
                       "longitude_max":lon+_off}
        _rect_lat = "%(latitude_field)s>%(latitude_min)s and %(latitude_field)s<%(latitude_max)s" \
                    % {"latitude_field":_latitude_field, "latitude_min":lat-_off, \
                       "latitude_max":lat+_off}
        _objects = _objects.extra(where = [ "".join([_rect_lon, " and ", _rect_lat])])

        cal_str0 = "("
        cal_str1 = "SIN((%(latitude_field)s-%(latitude_value)s)*3.1415926/180.0/2.0) \
                * SIN((%(latitude_field)s-%(latitude_value)s)*3.1415926/180.0/2.0)" \
                   % {"latitude_field":_latitude_field, "latitude_value":lat}
        cal_str2 = " + COS(%(latitude_field)s*3.1415926/180.0) \
                * COS(%(latitude_value)s*3.1415926/180.0) \
                * SIN((%(longitude_field)s-%(longitude_value)s)*3.1415926/180.0/2.0) \
                * SIN((%(longitude_field)s-%(longitude_value)s)*3.1415926/180.0/2.0)" \
                   % {"latitude_field":_latitude_field, "latitude_value":lat, \
                      "longitude_field":_longitude_field, "longitude_value":lon}
        cal_str3 = ")"
        cal_str = "".join([cal_str0, cal_str1, cal_str2, cal_str3])
        cal_dis_str = "ceil(2.0 * ATAN2(SQRT(%s), SQRT(1.0-%s)) * 6371135)" % (cal_str, cal_str)
        _objects = _objects.extra(select={"GPS_distance":cal_dis_str}, \
                                  where=["%s <= %s" % (cal_dis_str, offset)], \
                                  order_by=["GPS_distance"])

        if _objects is not None and _objects.count() and _keywords:
            _objects = self.query_keywords(_keywords=_keywords, \
                                           _query_set=_objects)
        if _order_by:
            _objects = self.ordered(_query_set=_objects, _order_by=_order_by)

        return _objects

    def ordered(self, _query_set = None, _order_by = None):
        '''
        对查询结果进行排序
        @param _query_set:查询结果集
        @param _order_by:排序类型，与self.order_type对应
        '''
        if _order_by is None:
            return _query_set

        if _order_by is not None and _query_set is not None and _query_set.count() > 0:
            _queryset_fields = dir(_query_set[0])
            _order_by_field = []
            __order_by = str(_order_by).split(',')
            for _order in __order_by:
                if not _order:
                    continue
                _order = int(_order)
                if _order in self.order_type.FIELDS:
                    __fields = self.order_type.FIELDS[_order]

                    if not isinstance(__fields, list) and not isinstance(__fields, tuple):
                        __fields = list(__fields)
                    for __field in __fields:
                        _tmp_field = __field
                        if _tmp_field.startswith("-"):
                            _tmp_field = _tmp_field[1:]
                        if _tmp_field in _queryset_fields:
                            _order_by_field.append(__field)

            if _order_by_field:
                _tmp_selected = []
                for _item in _order_by_field:
                    if _item not in _tmp_selected:
                        _tmp_selected.append(_item)

                _query_set = _query_set.order_by(*_tmp_selected)

        return _query_set

    def delete_by_id(self, _id):
        '''
        根据ID删除指定的数据项
        @param _id: 要删除的对象ID
        '''
        _object = None
        try:
            _object = self.get(_id = _id)
        except self.model_class.DoesNotExist:
            pass

        if _object:
            _object.delete()
            return True
        else:
            return False

    def delete_overdue(self, _id_in_result, _do_delete = True, **kargs):
        if _id_in_result is None:
            _id_in_result = []
        _data_in_db = self.query(**kargs)
        _id_in_db = [_item.id for _item in _data_in_db]
        _deleted_ids = list(set(_id_in_db).difference(_id_in_result))
        _update_ids = list(set(_id_in_result)).difference(_id_in_db)
        if _do_delete:
            n = 0
            while n < len(_deleted_ids):
                _delete_slice = _deleted_ids[n : n + 100]
                _delete_items = self.query_ids(_ids = _delete_slice)
                _delete_items.delete()
                n += 100

        return _deleted_ids, _update_ids

    def delete(self, **kargs):
        _data = self.query(**kargs)
        if _data is not None:
            _data.delete()

        return True

